Loading packages
Loading the data:
model_4_21_95wRAW = NULL
model_2_7_21_new = NULL
model_2_7_21_new <- read_excel("~/Desktop/sage/model_2_7_21_newV4.xlsx")
model_4_21_95wRAW <- model_2_7_21_new
#library(tidyverse)
#model_4_21_95wRAW %>% select_if(negate(is.numeric))
dim(model_2_7_21_new)
[1] 2077 87
summPreds <- function(inpPred,inpTruth,inpMetrNms=c("err","acc","sens","spec")) {
retVals <- numeric()
for ( metrTmp in inpMetrNms ) {
retVals[metrTmp] <- performance(prediction(inpPred,inpTruth),measure=metrTmp)@y.values[[1]][2]
}
retVals
}
Train = NULL
Test = NULL
bTrain = sample(1:nrow(model_4_21_95wRAW),round(0.7*nrow(model_4_21_95wRAW)))
Train <- model_4_21_95wRAW[bTrain,2:87]
Test <- model_4_21_95wRAW[-bTrain,2:87]
Test = as.data.frame(Test)
Train$cat = as.numeric(as.factor(Train$cat))
Test$cat = as.numeric(as.factor(Test$cat))
Y = factor(as.numeric(as.factor(Train$cat)))
tuneRF(Train[1:85],Y,ntreeTry = 300,plot=TRUE)
mtry = 9 OOB error = 0.96%
Searching left ...
mtry = 5 OOB error = 1.44%
-0.5 0.05
Searching right ...
mtry = 18 OOB error = 0.41%
0.5714286 0.05
mtry = 36 OOB error = 0.55%
-0.3333333 0.05
mtry OOBError
5.OOB 5 0.014442916
9.OOB 9 0.009628611
18.OOB 18 0.004126547
36.OOB 36 0.005502063

Y = factor(as.numeric(as.factor(Train$cat)))
rfTmp <- randomForest(Y~.,data=Train[,1:85],mTry = 18,ntree = 1000)
rfTestPred <- predict(rfTmp,newdata=Test[,1:85])
mseTest <- mean((as.numeric(factor(Test$cat))-as.numeric(rfTestPred))) #Calculating the mean squared error
#tmpVals <- summPreds(as.numeric(factor(Test$cat)),as.numeric(rfTestPred))
VI_F=importance(rfTmp)
VI_F
MeanDecreaseGini
LNTcon1 7.6357064
LNTcon2 7.6823166
LNTcon3 6.1456542
LNTfh1 3.3675898
LNTfh2 3.6810801
LNTfh3 3.7280283
LNTreg1 45.3698007
LNTreg2 26.8094780
LNTreg3 34.0956535
LNTfr1 47.1328080
LNTfr2 49.9809364
LNTfr3 72.5765562
SpleenTcon1 3.6301799
SpleenTcon2 5.0774485
SpleenTcon3 2.7495229
SpleenTfh1 1.7927661
SpleenTfh2 1.7451146
SpleenTfh3 3.2312894
SpleenTreg1 10.9382510
SpleenTreg2 16.5781411
SpleenTreg3 18.3344852
SpleenTfr1 12.5759806
SpleenTfr2 12.9644749
SpleenTfr3 8.4423328
BloodTcon1 8.8024146
BloodTcon2 8.4828076
BloodTcon3 7.2863457
BloodTfh1 1.4808955
BloodTfh2 1.5113150
BloodTfh3 1.4598444
BloodTreg1 5.1066351
BloodTreg2 6.0971699
BloodTreg3 7.1942529
BloodTfr1 1.0420599
BloodTfr2 0.9905301
BloodTfr3 1.0568475
EZH2EXP_F_p_Treg_1 4.5791186
EZH2EXP_F_p_Treg_2 2.0443356
EZH2EXP_F_p_Treg_3 2.1459223
EZH2EXP_F_p_Tfr_1 7.8041261
EZH2EXP_F_p_Tfr_2 10.2892409
EZH2EXP_F_p_Tfr_3 10.9562897
BlimpEXP_F_p_Treg_1 4.5235946
BlimpEXP_F_p_Treg_2 3.5205116
BlimpEXP_F_p_Treg_3 2.1943278
BlimpEXP_F_p_Treg_4 3.3664066
BlimpEXP_F_p_Tfr_1 2.3628381
BlimpEXP_F_p_Tfr_2 4.9722807
BlimpEXP_F_p_Tfr_3 2.6237975
BlimpEXP_F_p_Tfr_4 2.1307726
FoxDownFoxP_Tfr_1 3.7171745
FoxDownFoxP_Tfr_2 2.0298604
FoxDownFoxP_Tfr_3 2.3420473
FoxDownFoxMTfr_1 1.4743007
FoxDownFoxMTfr_2 1.3139286
FoxDownFoxMTfr_3 1.1419481
FoxDown_Tfh_1 5.2228072
FoxDown_Tfh_2 15.1654368
FoxDown_Tfh_3 3.5967246
HDM_OVA_Tcon_1 9.7500222
HDM_OVA_Tcon_2 6.9236944
HDM_OVA_Tfh_1 1.3556755
HDM_OVA_Tfh_2 1.9274904
HDM_OVA_Tfr_1 1.4817007
HDM_OVA_Tfr_2 1.3912696
HDM_Tfh_1 1.7202721
HDM_Tfh_2 1.3921791
HDM_Tfr_1 2.5219640
HDM_Tfr_2 6.9118782
FoxF_CreN_Tcon_1 10.5643154
FoxF_CreN_Tcon_2 6.3363880
FoxF_CreN_Tcon_3 2.9943886
FoxF_CreN_Tcon_4 3.6819194
FoxF_CreN_Treg_1 2.2946092
FoxF_CreN_Treg_2 4.4240561
FoxF_CreN_Treg_3 2.0410392
FoxF_CreN_Treg_4 4.2720484
FoxF_CreN_ICOS_1 22.9569124
FoxF_CreN_ICOS_2 16.9067668
FoxF_CreN_ICOS_3 13.8311513
FoxF_CreN_ICOS_4 26.2319491
FoxF_CreN_Tfr_1 3.7286656
FoxF_CreN_Tfr_2 1.4987874
FoxF_CreN_Tfr_3 10.1542357
FoxF_CreN_Tfr_4 2.9102014
#pdf("Importance_plot.pdf")
varImpPlot(rfTmp,type=2)

#dev.off()
# LNTreg1 43.447022
# LNTreg2 27.107419
# LNTreg3 32.783442
# LNTfr1 53.615951
# LNTfr2 52.316917
# LNTfr3 67.765825
# FoxF_CreN_ICOS_4 29.519462
# pdf("Importance_plot.pdf")
# partialPlot(rfTmp,x.var="LNTfr3",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="LNTfr1",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="LNTfr2",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="LNTreg1",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="LNTreg3",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="LNTreg2",pred.data = as.data.frame(Train),which.class = 2)
# partialPlot(rfTmp,x.var="FoxF_CreN_ICOS_4",pred.data = as.data.frame(Train),which.class = 2)
# dev.off()
partialPlot(rfTmp,x.var="BloodTfr1",pred.data = as.data.frame(Train),which.class = 2)

partialPlot(rfTmp,x.var="BloodTfr2",pred.data = as.data.frame(Train),which.class = 2)

partialPlot(rfTmp,x.var="BloodTfr3",pred.data = as.data.frame(Train),which.class = 2)

#
partialPlot(rfTmp,x.var="BloodTfr1",pred.data = as.data.frame(Train),which.class = 1)

partialPlot(rfTmp,x.var="BloodTfr2",pred.data = as.data.frame(Train),which.class = 1)

partialPlot(rfTmp,x.var="BloodTfr3",pred.data = as.data.frame(Train),which.class = 1)

#library(dplyr)
library(RColorBrewer)
imp <- as.data.frame(VI_F)
imp$varnames <- rownames(VI_F)
imp = imp %>% arrange(MeanDecreaseGini)
small = imp[65:85,]
#imp = order(imp)
dotchart(small$MeanDecreaseGini,labels = small$varnames,color = brewer.pal(n = 8, name = "Dark2"))

ggplot(data = small, aes(x = MeanDecreaseGini,y = reorder(varnames, MeanDecreaseGini))) +
geom_bar(stat="identity",fill = 'darkred')+
labs(y = "Condition") +
theme(legend.position = "top")

p = ggplot(small, aes(x = MeanDecreaseGini, y = reorder(varnames, MeanDecreaseGini))) +
geom_point(aes(color = MeanDecreaseGini, size = MeanDecreaseGini), alpha = 0.5)
p
ggsave(file="test.svg", plot=p, width=10, height=8)

library(readxl)
TfhTfr_Exon_AllGenes_combined <- read_excel("~/Desktop/sage/TfhTfr_Exon_AllGenes_combined.xlsx")
#Getting the class
#rfTestPred <- predict(rfTmp,newdata=TfhTfr_Exon_AllGenes_combined[,2:86])
rfTestPred <- predict(rfTmp,newdata=TfhTfr_Exon_AllGenes_combined[,2:86],type = "class")
#getting the probabilities
rfTestPred_prob <- predict(rfTmp,newdata=TfhTfr_Exon_AllGenes_combined[,2:86],type = "prob")
#Here I am trying to get some random names for the GSEA gene set.
random_names = character(2000)
random_names = sample(TfhTfr_Exon_AllGenes_combined$`Feature ID`,2000)
fileConn<-file("random_names.txt")
writeLines(random_names, fileConn)
close(fileConn)
2 is a Yes, 1 is a No
TfhTfr_Exon_AllGenes_combined = cbind(TfhTfr_Exon_AllGenes_combined,rfTestPred)
genes_vales = TfhTfr_Exon_AllGenes_combined[,c(1,87)]
genes_vales = cbind(genes_vales,rfTestPred_prob)
hist(as.numeric(genes_vales[,2]))

Here I generate an image of the final tree from the training/test data
#rfTestPred_prob
options(repos='http://cran.rstudio.org')
have.packages <- installed.packages()
cran.packages <- c('devtools','plotrix','randomForest','tree')
to.install <- setdiff(cran.packages, have.packages[,1])
if(length(to.install)>0) install.packages(to.install)
library(devtools)
Loading required package: usethis
if(!('reprtree' %in% installed.packages())){
install_github('araastat/reprtree')
}
for(p in c(cran.packages, 'reprtree')) eval(substitute(library(pkg), list(pkg=p)))
Attaching package: ‘plotrix’
The following object is masked from ‘package:psych’:
rescale
library(reprtree)
svg("TREE.svg", width=20, height=20,pointsize = 5)
reprtree:::plot.getTree(rfTmp)
dev.off()
null device
1
q-q plots of the p-values
qqnorm(rfTestPred_prob, pch = 1, frame = FALSE)
qqline(rfTestPred_prob, col = "steelblue", lwd = 2)

#genes_vales = cbind(genes_vales,rfTestPred_prob)
Yes_columns = NULL
Yes_columns = dplyr::filter( genes_vales, rfTestPred == 2 )
Yes_columns = data.frame(Yes_columns)
Yes_columns = Yes_columns[order(Yes_columns$X2,decreasing = TRUE),]
Yes_columns_sub = Yes_columns[1:2000,]
#View(Yes_columns_sub)
TfhTfr_Exon_AllGenes_combined_Yes = NULL
TfhTfr_Exon_AllGenes_combined_Yes = cbind(TfhTfr_Exon_AllGenes_combined,rfTestPred_prob)
TfhTfr_Exon_AllGenes_combined_Yes = dplyr::filter( TfhTfr_Exon_AllGenes_combined_Yes, rfTestPred == 2 )
TfhTfr_Exon_AllGenes_combined_Yes = data.frame(TfhTfr_Exon_AllGenes_combined_Yes)
TfhTfr_Exon_AllGenes_combined_Yes = TfhTfr_Exon_AllGenes_combined_Yes[order(TfhTfr_Exon_AllGenes_combined_Yes$X2,decreasing = TRUE),]
TfhTfr_Exon_AllGenes_combined_Yes_top_2000 = TfhTfr_Exon_AllGenes_combined_Yes[2:2000,]
library(pheatmap)
#small heatmap
genesList = c('Cxcr5','Icos','Pdcd1','Bcl6','Nrn1','Tnfrsf18','Gzmb','Sh2d1a','Prdm1','Cd44','Irf4','Ezh2','Il1r2','Cxcr7','Foxp3','Bcl6','Il2rg','Il2rb','Bcl6','Ctla4','Gata3','Id2','Il10','Maf')
colsToUse = 8:13
colsToUse = c(1,colsToUse)
McolsToUse = c(colsToUse,20:25) # Just Spleen
McolsToUse = c(McolsToUse,32:37) # Blood included
Tcon_McolsToUse = c(McolsToUse,2:4,14:16,26:28) # Tcon included
subsetYes = TfhTfr_Exon_AllGenes_combined_Yes[TfhTfr_Exon_AllGenes_combined_Yes$Feature.ID %in% genesList,colsToUse]
Copy_subsetYes = as.matrix(subsetYes[,2:7])
rownames(Copy_subsetYes) = subsetYes[,1]
p = pheatmap(Copy_subsetYes,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
p

ggsave(file="subListYesGenes.svg", plot=p, width=10, height=8)
#with more cell types
More_cell_types = TfhTfr_Exon_AllGenes_combined_Yes[TfhTfr_Exon_AllGenes_combined_Yes$Feature.ID %in% genesList,McolsToUse]
#MCopy_subsetYes = as.matrix(More_cell_types[,2:13]) # with just spleen
MCopy_subsetYes = as.matrix(More_cell_types[,2:19]) #Blood included
colsReorder = c(1:3,7:9,13:15,4:6,10:12,16:18)
MCopy_subsetYes_reOrder = MCopy_subsetYes[,colsReorder] #re-ordered cols
rownames(MCopy_subsetYes) = More_cell_types[,1]
rownames(MCopy_subsetYes_reOrder) = More_cell_types[,1]
View(MCopy_subsetYes_reOrder)
p = pheatmap(MCopy_subsetYes,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
p

RC_p = pheatmap(MCopy_subsetYes_reOrder,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
RC_p

ggsave(file="Proper_order_subListYesGenes_cell_types.svg", plot=RC_p, width=10, height=8)
###############
#adding Tcon
T_con_More_cell_types = TfhTfr_Exon_AllGenes_combined_Yes[TfhTfr_Exon_AllGenes_combined_Yes$Feature.ID %in% genesList,Tcon_McolsToUse]
#MCopy_subsetYes = as.matrix(More_cell_types[,2:13]) # with just spleen
T_con_MCopy_subsetYes = as.matrix(T_con_More_cell_types[,2:28]) #Tcon included
colsReorder = c(1:6,19:21,7:12,22:24,13:18,25:27)
T_con_MCopy_subsetYes_reOrder = T_con_MCopy_subsetYes[,colsReorder] #re-ordered cols
rownames(T_con_MCopy_subsetYes) = More_cell_types[,1]
rownames(T_con_MCopy_subsetYes_reOrder) = More_cell_types[,1]
View(T_con_MCopy_subsetYes_reOrder)
T_con_RC_p = pheatmap(T_con_MCopy_subsetYes_reOrder,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
T_con_RC_p

ggsave(file="Proper_order_subListYesGenes_cell_types_T_con_.svg", plot=T_con_RC_p, width=8, height=6)
##########
#large heatmap
holder = TfhTfr_Exon_AllGenes_combined_Yes_top_2000[1:50,8:13]
#View(holder)
holder = as.matrix(holder)
rownames(holder) = TfhTfr_Exon_AllGenes_combined_Yes_top_2000[1:50,1]
heatmap(holder)

p = pheatmap(holder,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
p

ggsave(file="test.svg", plot=p, width=10, height=8)
#large Top 50
holder = NULL
holder = TfhTfr_Exon_AllGenes_combined_Yes_top_2000[1:50,McolsToUse]
#View(holder)
holder = as.matrix(holder[,2:19])
rownames(holder) = TfhTfr_Exon_AllGenes_combined_Yes_top_2000[1:50,1]
heatmap(holder)

p = pheatmap(holder,cutree_rows = 2,cutree_cols = 8,scale = 'row',clustering_distance_cols = "canberra")
p

ggsave(file="test.svg", plot=p, width=10, height=8)
Here I am working on the hclust linkage to see if there is one that better fits the columns
MCopy_subsetYes = as.matrix(More_cell_types[,2:19]) #Blood included
colsReorder = c(1:3,7:9,13:15,4:6,10:12,16:18)
MCopy_subsetYes_reOrder = MCopy_subsetYes[,colsReorder] #re-ordered cols
rownames(MCopy_subsetYes) = More_cell_types[,1]
rownames(MCopy_subsetYes_reOrder) = More_cell_types[,1]
p = pheatmap(MCopy_subsetYes,cutree_rows = 2,cutree_cols = 2,scale = 'row')
p

RC_p = pheatmap(MCopy_subsetYes_reOrder,cutree_rows = 2,cutree_cols = 2,scale = 'row',clustering_distance_cols = "canberra")
RC_p

#method
#"euclidean", "maximum", "manhattan", "canberra", "binary" or "minkowski".
# Ward Hierarchical Clustering
d <- dist(MCopy_subsetYes_reOrder, method = "euclidean") # distance matrix
fit_WD <- hclust(d, method="ward.D")
fit_WD2 <- hclust(d, method="ward.D2")
fit_S <- hclust(d, method="single")
fit_C <- hclust(d, method="complete")
fit_A <- hclust(d, method="average")
fit_MC <- hclust(d, method="mcquitty")
fit_M <- hclust(d, method="median")
fit_CE <- hclust(d, method="centroid")
library(gplots)
#rownames(TfhTfr_Exon_AllGenes_combined_Yes) = TfhTfr_Exon_AllGenes_combined_Yes[,2]
Ymatrix = unlist(TfhTfr_Exon_AllGenes_combined_Yes[1:5018,2:86])
Ymatrix = as.numeric(Ymatrix)
Ymatrix = matrix(v,nrow = 5018,ncol = 84)
heatmap(Ymatrix,symm = F,main = "Heatmap test",labRow = rownames(TfhTfr_Exon_AllGenes_combined_Yes))
Making the top calls for the other class.
TfhTfr_Exon_AllGenes_combined_No = NULL
TfhTfr_Exon_AllGenes_combined_No = cbind(TfhTfr_Exon_AllGenes_combined,rfTestPred_prob)
TfhTfr_Exon_AllGenes_combined_No = dplyr::filter( TfhTfr_Exon_AllGenes_combined_No, rfTestPred == 1 )
TfhTfr_Exon_AllGenes_combined_No = data.frame(TfhTfr_Exon_AllGenes_combined_No)
TfhTfr_Exon_AllGenes_combined_No = TfhTfr_Exon_AllGenes_combined_No[order(TfhTfr_Exon_AllGenes_combined_No$X1,decreasing = TRUE),]
TfhTfr_Exon_AllGenes_combined_No_top_1000 = TfhTfr_Exon_AllGenes_combined_No[1:1000,]
Looking at the top 50 from each class
TfhTfr_Exon_AllGenes_combined_Yes_top_50 = TfhTfr_Exon_AllGenes_combined_Yes[1:50,]
TfhTfr_Exon_AllGenes_combined_No_top_50 = TfhTfr_Exon_AllGenes_combined_No[1:50,]
groupCalls = rbind(TfhTfr_Exon_AllGenes_combined_Yes_top_50,TfhTfr_Exon_AllGenes_combined_No_top_50)
YNHeatmap_matrix = NULL
YNHeatmap_matrix = unlist(groupCalls[2:100,2:86])
YNHeatmap_matrix = as.numeric(v)
YNHeatmap_matrix = matrix(v,nrow = 98,ncol = 84)
heatmap(YNHeatmap_matrix,symm = F,main = "Heatmap test",labRow = rownames(groupCalls))
image(YNHeatmap_matrix)
write.csv(as.data.frame(Yes_columns_sub),file = "TFR_model_results_top_1000_genes.csv")
write.csv(as.data.frame(TfhTfr_Exon_AllGenes_combined_Yes),file = "TFR_model_results_Yes_genes.csv")
TfhTfr_Exon_AllGenes_combined_Yes_top_2000
write.csv(as.data.frame(TfhTfr_Exon_AllGenes_combined_Yes_top_2000),file = "TFR_model_results_Yes_genes_top_2000.csv")
write.csv(as.data.frame(TfhTfr_Exon_AllGenes_combined_No_top_1000),file = "TFR_model_results_No_genes_1000.csv")
write.csv(as.data.frame(TfhTfr_Exon_AllGenes_combined_No_top_1000),file = "TFR_model_results_No_genes_1000.csv")
write.csv(as.data.frame(groupCalls),file = "TFR_model_results_group.csv")
Not used
#BiocManager::install("M3C")
#library(M3C)
YmatrixNew = Ymatrix
#colnames(YmatrixNew) = colnames(TfhTfr_Exon_AllGenes_combined_Yes[,2:85])
umap(TfhTfr_Exon_AllGenes_combined_Yes[,2:86],colvec = as.factor(colnames(TfhTfr_Exon_AllGenes_combined_Yes[,2:86])),labels = as.factor(colnames(TfhTfr_Exon_AllGenes_combined_Yes[,2:86])))
#tfr.umap = umap(Ymatrix)
#tfr.umap
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKTG9hZGluZyBwYWNrYWdlcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShuZXVyYWxuZXQpCmxpYnJhcnkocmVhZHhsKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoY29ycnBsb3QpCmxpYnJhcnkoY2FyZXQpCmxpYnJhcnkoZTEwNzEpCmxpYnJhcnkoUk9DUikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KEdHYWxseSkKbGlicmFyeShQZXJmb3JtYW5jZUFuYWx5dGljcykKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KGNvcnJwbG90KQpsaWJyYXJ5KFJ0c25lKQpsaWJyYXJ5KEZhY3RvTWluZVIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShmYWN0b2V4dHJhKQpsaWJyYXJ5KHN1cnZtaW5lcikKbGlicmFyeShnZ2NvcnJwbG90KQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHJlc2hhcGUpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoQ29tcGxleEhlYXRtYXApCmxpYnJhcnkoZ2dwdWJyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoZml0ZGlzdHJwbHVzKQojQmlvY01hbmFnZXI6Omluc3RhbGwoInByZXByb2Nlc3NDb3JlIikKbGlicmFyeShwcmVwcm9jZXNzQ29yZSkKbGlicmFyeShNQVNTKQpsaWJyYXJ5KGNsYXNzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocmVzaGFwZTIpCmxpYnJhcnkoUk9DUikKbGlicmFyeShlMTA3MSkKbGlicmFyeShHR2FsbHkpCmxpYnJhcnkoa2xhUikKbGlicmFyeShyYW5kb21Gb3Jlc3QpCgpgYGAKCkxvYWRpbmcgdGhlIGRhdGE6CgoKYGBge3J9Cm1vZGVsXzRfMjFfOTV3UkFXID0gTlVMTAptb2RlbF8yXzdfMjFfbmV3ID0gTlVMTAptb2RlbF8yXzdfMjFfbmV3IDwtIHJlYWRfZXhjZWwoIn4vRGVza3RvcC9zYWdlL21vZGVsXzJfN18yMV9uZXdWNC54bHN4IikKCm1vZGVsXzRfMjFfOTV3UkFXIDwtIG1vZGVsXzJfN18yMV9uZXcKI2xpYnJhcnkodGlkeXZlcnNlKQojbW9kZWxfNF8yMV85NXdSQVcgJT4lIHNlbGVjdF9pZihuZWdhdGUoaXMubnVtZXJpYykpCgpkaW0obW9kZWxfMl83XzIxX25ldykKYGBgCgpgYGB7cn0Kc3VtbVByZWRzIDwtIGZ1bmN0aW9uKGlucFByZWQsaW5wVHJ1dGgsaW5wTWV0ck5tcz1jKCJlcnIiLCJhY2MiLCJzZW5zIiwic3BlYyIpKSB7CiAgcmV0VmFscyA8LSBudW1lcmljKCkKICBmb3IgKCBtZXRyVG1wIGluIGlucE1ldHJObXMgKSB7CiAgICByZXRWYWxzW21ldHJUbXBdIDwtIHBlcmZvcm1hbmNlKHByZWRpY3Rpb24oaW5wUHJlZCxpbnBUcnV0aCksbWVhc3VyZT1tZXRyVG1wKUB5LnZhbHVlc1tbMV1dWzJdCiAgfQogIHJldFZhbHMKfQpgYGAKCgpgYGB7cn0KICAgIFRyYWluID0gTlVMTAogICAgVGVzdCA9IE5VTEwKICAgIGJUcmFpbiA9IHNhbXBsZSgxOm5yb3cobW9kZWxfNF8yMV85NXdSQVcpLHJvdW5kKDAuNypucm93KG1vZGVsXzRfMjFfOTV3UkFXKSkpCiAgICBUcmFpbiA8LSBtb2RlbF80XzIxXzk1d1JBV1tiVHJhaW4sMjo4N10KICAgIFRlc3QgPC0gbW9kZWxfNF8yMV85NXdSQVdbLWJUcmFpbiwyOjg3XQogICAgVGVzdCA9IGFzLmRhdGEuZnJhbWUoVGVzdCkKICAgICAgICAgICAgICBUcmFpbiRjYXQgPSBhcy5udW1lcmljKGFzLmZhY3RvcihUcmFpbiRjYXQpKQogICAgICAgICAgVGVzdCRjYXQgPSBhcy5udW1lcmljKGFzLmZhY3RvcihUZXN0JGNhdCkpCiAgICAgICAgICAKICAgICAgICAgIApgYGAKCmBgYHtyfQpZID0gZmFjdG9yKGFzLm51bWVyaWMoYXMuZmFjdG9yKFRyYWluJGNhdCkpKQp0dW5lUkYoVHJhaW5bMTo4NV0sWSxudHJlZVRyeSA9IDMwMCxwbG90PVRSVUUpCmBgYAoKCgpgYGB7ciBlY2hvPVRSVUV9ClkgPSBmYWN0b3IoYXMubnVtZXJpYyhhcy5mYWN0b3IoVHJhaW4kY2F0KSkpCnJmVG1wIDwtIHJhbmRvbUZvcmVzdChZfi4sZGF0YT1UcmFpblssMTo4NV0sbVRyeSA9IDE4LG50cmVlID0gMTAwMCkKcmZUZXN0UHJlZCA8LSBwcmVkaWN0KHJmVG1wLG5ld2RhdGE9VGVzdFssMTo4NV0pCgoKbXNlVGVzdCA8LSBtZWFuKChhcy5udW1lcmljKGZhY3RvcihUZXN0JGNhdCkpLWFzLm51bWVyaWMocmZUZXN0UHJlZCkpKSAjQ2FsY3VsYXRpbmcgdGhlIG1lYW4gc3F1YXJlZCBlcnJvcgojdG1wVmFscyA8LSBzdW1tUHJlZHMoYXMubnVtZXJpYyhmYWN0b3IoVGVzdCRjYXQpKSxhcy5udW1lcmljKHJmVGVzdFByZWQpKQoKVklfRj1pbXBvcnRhbmNlKHJmVG1wKQpWSV9GCiNwZGYoIkltcG9ydGFuY2VfcGxvdC5wZGYiKQp2YXJJbXBQbG90KHJmVG1wLHR5cGU9MikKI2Rldi5vZmYoKQoKIyBMTlRyZWcxICAgICAgICAgICAgICAgICAgICA0My40NDcwMjIKIyBMTlRyZWcyICAgICAgICAgICAgICAgICAgICAyNy4xMDc0MTkKIyBMTlRyZWczICAgICAgICAgICAgICAgICAgICAzMi43ODM0NDIKIyBMTlRmcjEgICAgICAgICAgICAgICAgICAgICA1My42MTU5NTEKIyBMTlRmcjIgICAgICAgICAgICAgICAgICAgICA1Mi4zMTY5MTcKIyBMTlRmcjMgICAgICAgICAgICAgICAgICAgICA2Ny43NjU4MjUKIyBGb3hGX0NyZU5fSUNPU180ICAgICAgICAgICAyOS41MTk0NjIKIyBwZGYoIkltcG9ydGFuY2VfcGxvdC5wZGYiKQojIHBhcnRpYWxQbG90KHJmVG1wLHgudmFyPSJMTlRmcjMiLHByZWQuZGF0YSA9IGFzLmRhdGEuZnJhbWUoVHJhaW4pLHdoaWNoLmNsYXNzID0gMikKIyBwYXJ0aWFsUGxvdChyZlRtcCx4LnZhcj0iTE5UZnIxIixwcmVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKFRyYWluKSx3aGljaC5jbGFzcyA9IDIpCiMgcGFydGlhbFBsb3QocmZUbXAseC52YXI9IkxOVGZyMiIscHJlZC5kYXRhID0gYXMuZGF0YS5mcmFtZShUcmFpbiksd2hpY2guY2xhc3MgPSAyKQojIHBhcnRpYWxQbG90KHJmVG1wLHgudmFyPSJMTlRyZWcxIixwcmVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKFRyYWluKSx3aGljaC5jbGFzcyA9IDIpCiMgcGFydGlhbFBsb3QocmZUbXAseC52YXI9IkxOVHJlZzMiLHByZWQuZGF0YSA9IGFzLmRhdGEuZnJhbWUoVHJhaW4pLHdoaWNoLmNsYXNzID0gMikKIyBwYXJ0aWFsUGxvdChyZlRtcCx4LnZhcj0iTE5UcmVnMiIscHJlZC5kYXRhID0gYXMuZGF0YS5mcmFtZShUcmFpbiksd2hpY2guY2xhc3MgPSAyKQojIHBhcnRpYWxQbG90KHJmVG1wLHgudmFyPSJGb3hGX0NyZU5fSUNPU180IixwcmVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKFRyYWluKSx3aGljaC5jbGFzcyA9IDIpCiMgZGV2Lm9mZigpCnBhcnRpYWxQbG90KHJmVG1wLHgudmFyPSJCbG9vZFRmcjEiLHByZWQuZGF0YSA9IGFzLmRhdGEuZnJhbWUoVHJhaW4pLHdoaWNoLmNsYXNzID0gMikKcGFydGlhbFBsb3QocmZUbXAseC52YXI9IkJsb29kVGZyMiIscHJlZC5kYXRhID0gYXMuZGF0YS5mcmFtZShUcmFpbiksd2hpY2guY2xhc3MgPSAyKQpwYXJ0aWFsUGxvdChyZlRtcCx4LnZhcj0iQmxvb2RUZnIzIixwcmVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKFRyYWluKSx3aGljaC5jbGFzcyA9IDIpCiMgCnBhcnRpYWxQbG90KHJmVG1wLHgudmFyPSJCbG9vZFRmcjEiLHByZWQuZGF0YSA9IGFzLmRhdGEuZnJhbWUoVHJhaW4pLHdoaWNoLmNsYXNzID0gMSkKcGFydGlhbFBsb3QocmZUbXAseC52YXI9IkJsb29kVGZyMiIscHJlZC5kYXRhID0gYXMuZGF0YS5mcmFtZShUcmFpbiksd2hpY2guY2xhc3MgPSAxKQpwYXJ0aWFsUGxvdChyZlRtcCx4LnZhcj0iQmxvb2RUZnIzIixwcmVkLmRhdGEgPSBhcy5kYXRhLmZyYW1lKFRyYWluKSx3aGljaC5jbGFzcyA9IDEpCgojbGlicmFyeShkcGx5cikKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmltcCA8LSBhcy5kYXRhLmZyYW1lKFZJX0YpCmltcCR2YXJuYW1lcyA8LSByb3duYW1lcyhWSV9GKQppbXAgPSBpbXAgJT4lIGFycmFuZ2UoTWVhbkRlY3JlYXNlR2luaSkKc21hbGwgPSBpbXBbNjU6ODUsXQojaW1wID0gb3JkZXIoaW1wKQpkb3RjaGFydChzbWFsbCRNZWFuRGVjcmVhc2VHaW5pLGxhYmVscyA9IHNtYWxsJHZhcm5hbWVzLGNvbG9yID0gYnJld2VyLnBhbChuID0gOCwgbmFtZSA9ICJEYXJrMiIpKQoKCmdncGxvdChkYXRhID0gc21hbGwsIGFlcyh4ID0gTWVhbkRlY3JlYXNlR2luaSx5ID0gcmVvcmRlcih2YXJuYW1lcywgTWVhbkRlY3JlYXNlR2luaSkpKSArCmdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IixmaWxsID0gJ2RhcmtyZWQnKSsKbGFicyh5ID0gIkNvbmRpdGlvbiIpICsKdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInRvcCIpCgoKCnAgPSBnZ3Bsb3Qoc21hbGwsIGFlcyh4ID0gTWVhbkRlY3JlYXNlR2luaSwgeSA9IHJlb3JkZXIodmFybmFtZXMsIE1lYW5EZWNyZWFzZUdpbmkpKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gTWVhbkRlY3JlYXNlR2luaSwgc2l6ZSA9IE1lYW5EZWNyZWFzZUdpbmkpLCBhbHBoYSA9IDAuNSkgCiAgcAogZ2dzYXZlKGZpbGU9InRlc3Quc3ZnIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTgpCgpgYGAKCgoKYGBge3J9CmxpYnJhcnkocmVhZHhsKQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZCA8LSByZWFkX2V4Y2VsKCJ+L0Rlc2t0b3Avc2FnZS9UZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZC54bHN4IikKCiNHZXR0aW5nIHRoZSBjbGFzcwojcmZUZXN0UHJlZCA8LSBwcmVkaWN0KHJmVG1wLG5ld2RhdGE9VGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRbLDI6ODZdKQpyZlRlc3RQcmVkIDwtIHByZWRpY3QocmZUbXAsbmV3ZGF0YT1UZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZFssMjo4Nl0sdHlwZSA9ICJjbGFzcyIpCiNnZXR0aW5nIHRoZSBwcm9iYWJpbGl0aWVzCnJmVGVzdFByZWRfcHJvYiA8LSBwcmVkaWN0KHJmVG1wLG5ld2RhdGE9VGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRbLDI6ODZdLHR5cGUgPSAicHJvYiIpCgojSGVyZSBJIGFtIHRyeWluZyB0byBnZXQgc29tZSByYW5kb20gbmFtZXMgZm9yIHRoZSBHU0VBIGdlbmUgc2V0LgpyYW5kb21fbmFtZXMgPSBjaGFyYWN0ZXIoMjAwMCkKcmFuZG9tX25hbWVzID0gc2FtcGxlKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkJGBGZWF0dXJlIElEYCwyMDAwKQpmaWxlQ29ubjwtZmlsZSgicmFuZG9tX25hbWVzLnR4dCIpCndyaXRlTGluZXMocmFuZG9tX25hbWVzLCBmaWxlQ29ubikKY2xvc2UoZmlsZUNvbm4pCmBgYAoKMiBpcyBhIFllcywgMSBpcyBhIE5vCmBgYHtyfQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZCA9IGNiaW5kKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkLHJmVGVzdFByZWQpCmdlbmVzX3ZhbGVzID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRbLGMoMSw4NyldCmdlbmVzX3ZhbGVzID0gY2JpbmQoZ2VuZXNfdmFsZXMscmZUZXN0UHJlZF9wcm9iKQoKaGlzdChhcy5udW1lcmljKGdlbmVzX3ZhbGVzWywyXSkpCmBgYAoKSGVyZSBJIGdlbmVyYXRlIGFuIGltYWdlIG9mIHRoZSBmaW5hbCB0cmVlIGZyb20gdGhlIHRyYWluaW5nL3Rlc3QgZGF0YQpgYGB7cn0KI3JmVGVzdFByZWRfcHJvYgpvcHRpb25zKHJlcG9zPSdodHRwOi8vY3Jhbi5yc3R1ZGlvLm9yZycpCmhhdmUucGFja2FnZXMgPC0gaW5zdGFsbGVkLnBhY2thZ2VzKCkKY3Jhbi5wYWNrYWdlcyA8LSBjKCdkZXZ0b29scycsJ3Bsb3RyaXgnLCdyYW5kb21Gb3Jlc3QnLCd0cmVlJykKdG8uaW5zdGFsbCA8LSBzZXRkaWZmKGNyYW4ucGFja2FnZXMsIGhhdmUucGFja2FnZXNbLDFdKQppZihsZW5ndGgodG8uaW5zdGFsbCk+MCkgaW5zdGFsbC5wYWNrYWdlcyh0by5pbnN0YWxsKQoKbGlicmFyeShkZXZ0b29scykKaWYoISgncmVwcnRyZWUnICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpKXsKICBpbnN0YWxsX2dpdGh1YignYXJhYXN0YXQvcmVwcnRyZWUnKQp9CmZvcihwIGluIGMoY3Jhbi5wYWNrYWdlcywgJ3JlcHJ0cmVlJykpIGV2YWwoc3Vic3RpdHV0ZShsaWJyYXJ5KHBrZyksIGxpc3QocGtnPXApKSkKbGlicmFyeShyZXBydHJlZSkKCnN2ZygiVFJFRS5zdmciLCAgICB3aWR0aD0yMCwgaGVpZ2h0PTIwLHBvaW50c2l6ZSA9IDUpCnJlcHJ0cmVlOjo6cGxvdC5nZXRUcmVlKHJmVG1wKQpkZXYub2ZmKCkKCmBgYAoKcS1xIHBsb3RzIG9mIHRoZSBwLXZhbHVlcwpgYGB7cn0KcXFub3JtKHJmVGVzdFByZWRfcHJvYiwgcGNoID0gMSwgZnJhbWUgPSBGQUxTRSkKcXFsaW5lKHJmVGVzdFByZWRfcHJvYiwgY29sID0gInN0ZWVsYmx1ZSIsIGx3ZCA9IDIpCmBgYAoKCmBgYHtyfQojZ2VuZXNfdmFsZXMgPSBjYmluZChnZW5lc192YWxlcyxyZlRlc3RQcmVkX3Byb2IpClllc19jb2x1bW5zID0gTlVMTApZZXNfY29sdW1ucyA9IGRwbHlyOjpmaWx0ZXIoIGdlbmVzX3ZhbGVzLCByZlRlc3RQcmVkID09IDIgKQpZZXNfY29sdW1ucyA9IGRhdGEuZnJhbWUoWWVzX2NvbHVtbnMpClllc19jb2x1bW5zID0gWWVzX2NvbHVtbnNbb3JkZXIoWWVzX2NvbHVtbnMkWDIsZGVjcmVhc2luZyA9IFRSVUUpLF0KWWVzX2NvbHVtbnNfc3ViID0gWWVzX2NvbHVtbnNbMToyMDAwLF0KI1ZpZXcoWWVzX2NvbHVtbnNfc3ViKQoKClRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcyA9IE5VTEwKVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzID0gY2JpbmQoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWQscmZUZXN0UHJlZF9wcm9iKQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXMgPSBkcGx5cjo6ZmlsdGVyKCBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXMsIHJmVGVzdFByZWQgPT0gMiApClRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcyA9IGRhdGEuZnJhbWUoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzKQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXMgPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNbb3JkZXIoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzJFgyLGRlY3JlYXNpbmcgPSBUUlVFKSxdClRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc190b3BfMjAwMCA9IFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc1syOjIwMDAsXQoKYGBgCgpgYGB7cn0KbGlicmFyeShwaGVhdG1hcCkKCiNzbWFsbCBoZWF0bWFwCmdlbmVzTGlzdCA9IGMoJ0N4Y3I1JywnSWNvcycsJ1BkY2QxJywnQmNsNicsJ05ybjEnLCdUbmZyc2YxOCcsJ0d6bWInLCdTaDJkMWEnLCdQcmRtMScsJ0NkNDQnLCdJcmY0JywnRXpoMicsJ0lsMXIyJywnQ3hjcjcnLCdGb3hwMycsJ0JjbDYnLCdJbDJyZycsJ0lsMnJiJywnQmNsNicsJ0N0bGE0JywnR2F0YTMnLCdJZDInLCdJbDEwJywnTWFmJykKCgpjb2xzVG9Vc2UgPSA4OjEzCmNvbHNUb1VzZSA9IGMoMSxjb2xzVG9Vc2UpCk1jb2xzVG9Vc2UgPSBjKGNvbHNUb1VzZSwyMDoyNSkgIyBKdXN0IFNwbGVlbgpNY29sc1RvVXNlID0gYyhNY29sc1RvVXNlLDMyOjM3KSAjIEJsb29kIGluY2x1ZGVkClRjb25fTWNvbHNUb1VzZSA9IGMoTWNvbHNUb1VzZSwyOjQsMTQ6MTYsMjY6MjgpICMgVGNvbiBpbmNsdWRlZAoKc3Vic2V0WWVzID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzW1RmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcyRGZWF0dXJlLklEICVpbiUgZ2VuZXNMaXN0LGNvbHNUb1VzZV0KCkNvcHlfc3Vic2V0WWVzID0gYXMubWF0cml4KHN1YnNldFllc1ssMjo3XSkKcm93bmFtZXMoQ29weV9zdWJzZXRZZXMpID0gc3Vic2V0WWVzWywxXQpwID0gcGhlYXRtYXAoQ29weV9zdWJzZXRZZXMsY3V0cmVlX3Jvd3MgPSAyLGN1dHJlZV9jb2xzID0gMixzY2FsZSA9ICdyb3cnLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjYW5iZXJyYSIpCnAKZ2dzYXZlKGZpbGU9InN1Ykxpc3RZZXNHZW5lcy5zdmciLCBwbG90PXAsIHdpZHRoPTEwLCBoZWlnaHQ9OCkKCiN3aXRoIG1vcmUgY2VsbCB0eXBlcwpNb3JlX2NlbGxfdHlwZXMgPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNbVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzJEZlYXR1cmUuSUQgJWluJSBnZW5lc0xpc3QsTWNvbHNUb1VzZV0KI01Db3B5X3N1YnNldFllcyA9IGFzLm1hdHJpeChNb3JlX2NlbGxfdHlwZXNbLDI6MTNdKSAjIHdpdGgganVzdCBzcGxlZW4KTUNvcHlfc3Vic2V0WWVzID0gYXMubWF0cml4KE1vcmVfY2VsbF90eXBlc1ssMjoxOV0pICNCbG9vZCBpbmNsdWRlZApjb2xzUmVvcmRlciA9IGMoMTozLDc6OSwxMzoxNSw0OjYsMTA6MTIsMTY6MTgpCk1Db3B5X3N1YnNldFllc19yZU9yZGVyID0gTUNvcHlfc3Vic2V0WWVzWyxjb2xzUmVvcmRlcl0gI3JlLW9yZGVyZWQgY29scwpyb3duYW1lcyhNQ29weV9zdWJzZXRZZXMpID0gTW9yZV9jZWxsX3R5cGVzWywxXQpyb3duYW1lcyhNQ29weV9zdWJzZXRZZXNfcmVPcmRlcikgPSBNb3JlX2NlbGxfdHlwZXNbLDFdClZpZXcoTUNvcHlfc3Vic2V0WWVzX3JlT3JkZXIpCnAgPSBwaGVhdG1hcChNQ29weV9zdWJzZXRZZXMsY3V0cmVlX3Jvd3MgPSAyLGN1dHJlZV9jb2xzID0gMixzY2FsZSA9ICdyb3cnLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjYW5iZXJyYSIpCnAKClJDX3AgPSBwaGVhdG1hcChNQ29weV9zdWJzZXRZZXNfcmVPcmRlcixjdXRyZWVfcm93cyA9IDIsY3V0cmVlX2NvbHMgPSAyLHNjYWxlID0gJ3JvdycsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNhbmJlcnJhIikKUkNfcApnZ3NhdmUoZmlsZT0iUHJvcGVyX29yZGVyX3N1Ykxpc3RZZXNHZW5lc19jZWxsX3R5cGVzLnN2ZyIsIHBsb3Q9UkNfcCwgd2lkdGg9MTAsIGhlaWdodD04KQoKIyMjIyMjIyMjIyMjIyMjCgojYWRkaW5nIFRjb24KVF9jb25fTW9yZV9jZWxsX3R5cGVzID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzW1RmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcyRGZWF0dXJlLklEICVpbiUgZ2VuZXNMaXN0LFRjb25fTWNvbHNUb1VzZV0KI01Db3B5X3N1YnNldFllcyA9IGFzLm1hdHJpeChNb3JlX2NlbGxfdHlwZXNbLDI6MTNdKSAjIHdpdGgganVzdCBzcGxlZW4KVF9jb25fTUNvcHlfc3Vic2V0WWVzID0gYXMubWF0cml4KFRfY29uX01vcmVfY2VsbF90eXBlc1ssMjoyOF0pICNUY29uIGluY2x1ZGVkCmNvbHNSZW9yZGVyID0gYygxOjYsMTk6MjEsNzoxMiwyMjoyNCwxMzoxOCwyNToyNykKVF9jb25fTUNvcHlfc3Vic2V0WWVzX3JlT3JkZXIgPSBUX2Nvbl9NQ29weV9zdWJzZXRZZXNbLGNvbHNSZW9yZGVyXSAjcmUtb3JkZXJlZCBjb2xzCnJvd25hbWVzKFRfY29uX01Db3B5X3N1YnNldFllcykgPSBNb3JlX2NlbGxfdHlwZXNbLDFdCnJvd25hbWVzKFRfY29uX01Db3B5X3N1YnNldFllc19yZU9yZGVyKSA9IE1vcmVfY2VsbF90eXBlc1ssMV0KVmlldyhUX2Nvbl9NQ29weV9zdWJzZXRZZXNfcmVPcmRlcikKClRfY29uX1JDX3AgPSBwaGVhdG1hcChUX2Nvbl9NQ29weV9zdWJzZXRZZXNfcmVPcmRlcixjdXRyZWVfcm93cyA9IDIsY3V0cmVlX2NvbHMgPSAyLHNjYWxlID0gJ3JvdycsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNhbmJlcnJhIikKVF9jb25fUkNfcApnZ3NhdmUoZmlsZT0iUHJvcGVyX29yZGVyX3N1Ykxpc3RZZXNHZW5lc19jZWxsX3R5cGVzX1RfY29uXy5zdmciLCBwbG90PVRfY29uX1JDX3AsIHdpZHRoPTgsIGhlaWdodD02KQoKIyMjIyMjIyMjIwojbGFyZ2UgaGVhdG1hcApob2xkZXIgPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNfdG9wXzIwMDBbMTo1MCw4OjEzXQojVmlldyhob2xkZXIpCmhvbGRlciA9IGFzLm1hdHJpeChob2xkZXIpCgpyb3duYW1lcyhob2xkZXIpID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzX3RvcF8yMDAwWzE6NTAsMV0KCgpoZWF0bWFwKGhvbGRlcikKCnAgPSBwaGVhdG1hcChob2xkZXIsY3V0cmVlX3Jvd3MgPSAyLGN1dHJlZV9jb2xzID0gMixzY2FsZSA9ICdyb3cnLGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9ICJjYW5iZXJyYSIpCnAKZ2dzYXZlKGZpbGU9InRlc3Quc3ZnIiwgcGxvdD1wLCB3aWR0aD0xMCwgaGVpZ2h0PTgpCgojbGFyZ2UgVG9wIDUwCmhvbGRlciA9IE5VTEwKaG9sZGVyID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzX3RvcF8yMDAwWzE6NTAsTWNvbHNUb1VzZV0KI1ZpZXcoaG9sZGVyKQpob2xkZXIgPSBhcy5tYXRyaXgoaG9sZGVyWywyOjE5XSkKCnJvd25hbWVzKGhvbGRlcikgPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNfdG9wXzIwMDBbMTo1MCwxXQoKCmhlYXRtYXAoaG9sZGVyKQoKcCA9IHBoZWF0bWFwKGhvbGRlcixjdXRyZWVfcm93cyA9IDIsY3V0cmVlX2NvbHMgPSA4LHNjYWxlID0gJ3JvdycsY2x1c3RlcmluZ19kaXN0YW5jZV9jb2xzID0gImNhbmJlcnJhIikKcApnZ3NhdmUoZmlsZT0idGVzdC5zdmciLCBwbG90PXAsIHdpZHRoPTEwLCBoZWlnaHQ9OCkKCmBgYAoKSGVyZSBJIGFtIHdvcmtpbmcgb24gdGhlIGhjbHVzdCBsaW5rYWdlIHRvIHNlZSBpZiB0aGVyZSBpcyBvbmUgdGhhdCBiZXR0ZXIgZml0cyB0aGUgY29sdW1ucwpgYGB7cn0KTUNvcHlfc3Vic2V0WWVzID0gYXMubWF0cml4KE1vcmVfY2VsbF90eXBlc1ssMjoxOV0pICNCbG9vZCBpbmNsdWRlZApjb2xzUmVvcmRlciA9IGMoMTozLDc6OSwxMzoxNSw0OjYsMTA6MTIsMTY6MTgpCk1Db3B5X3N1YnNldFllc19yZU9yZGVyID0gTUNvcHlfc3Vic2V0WWVzWyxjb2xzUmVvcmRlcl0gI3JlLW9yZGVyZWQgY29scwpyb3duYW1lcyhNQ29weV9zdWJzZXRZZXMpID0gTW9yZV9jZWxsX3R5cGVzWywxXQpyb3duYW1lcyhNQ29weV9zdWJzZXRZZXNfcmVPcmRlcikgPSBNb3JlX2NlbGxfdHlwZXNbLDFdCgpwID0gcGhlYXRtYXAoTUNvcHlfc3Vic2V0WWVzLGN1dHJlZV9yb3dzID0gMixjdXRyZWVfY29scyA9IDIsc2NhbGUgPSAncm93JykKcAoKUkNfcCA9IHBoZWF0bWFwKE1Db3B5X3N1YnNldFllc19yZU9yZGVyLGN1dHJlZV9yb3dzID0gMixjdXRyZWVfY29scyA9IDIsc2NhbGUgPSAncm93JyxjbHVzdGVyaW5nX2Rpc3RhbmNlX2NvbHMgPSAiY2FuYmVycmEiKQpSQ19wCgojbWV0aG9kCQogIyJldWNsaWRlYW4iLCAibWF4aW11bSIsICJtYW5oYXR0YW4iLCAiY2FuYmVycmEiLCAiYmluYXJ5IiBvciAibWlua293c2tpIi4gCgojIFdhcmQgSGllcmFyY2hpY2FsIENsdXN0ZXJpbmcKZCA8LSBkaXN0KE1Db3B5X3N1YnNldFllc19yZU9yZGVyLCBtZXRob2QgPSAiZXVjbGlkZWFuIikgIyBkaXN0YW5jZSBtYXRyaXgKZml0X1dEIDwtIGhjbHVzdChkLCBtZXRob2Q9IndhcmQuRCIpIApmaXRfV0QyIDwtIGhjbHVzdChkLCBtZXRob2Q9IndhcmQuRDIiKSAKZml0X1MgPC0gaGNsdXN0KGQsIG1ldGhvZD0ic2luZ2xlIikgCmZpdF9DIDwtIGhjbHVzdChkLCBtZXRob2Q9ImNvbXBsZXRlIikgCmZpdF9BIDwtIGhjbHVzdChkLCBtZXRob2Q9ImF2ZXJhZ2UiKSAKZml0X01DIDwtIGhjbHVzdChkLCBtZXRob2Q9Im1jcXVpdHR5IikgCmZpdF9NIDwtIGhjbHVzdChkLCBtZXRob2Q9Im1lZGlhbiIpCmZpdF9DRSA8LSBoY2x1c3QoZCwgbWV0aG9kPSJjZW50cm9pZCIpIAoKCgpgYGAKCgoKCmBgYHtyfQoKbGlicmFyeShncGxvdHMpCiNyb3duYW1lcyhUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXMpID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzWywyXQpZbWF0cml4ID0gdW5saXN0KFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc1sxOjUwMTgsMjo4Nl0pClltYXRyaXggPSBhcy5udW1lcmljKFltYXRyaXgpClltYXRyaXggPSBtYXRyaXgodixucm93ID0gNTAxOCxuY29sID0gODQpCgpoZWF0bWFwKFltYXRyaXgsc3ltbSA9IEYsbWFpbiA9ICJIZWF0bWFwIHRlc3QiLGxhYlJvdyA9IHJvd25hbWVzKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcykpCmBgYAoKTWFraW5nIHRoZSB0b3AgY2FsbHMgZm9yIHRoZSBvdGhlciBjbGFzcy4KCmBgYHtyfQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ObyA9IE5VTEwKVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfTm8gPSBjYmluZChUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZCxyZlRlc3RQcmVkX3Byb2IpClRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX05vID0gZHBseXI6OmZpbHRlciggVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfTm8sIHJmVGVzdFByZWQgPT0gMSApClRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX05vID0gZGF0YS5mcmFtZShUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ObykKVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfTm8gPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob1tvcmRlcihUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ObyRYMSxkZWNyZWFzaW5nID0gVFJVRSksXQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob190b3BfMTAwMCA9IFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX05vWzE6MTAwMCxdCgpgYGAKCkxvb2tpbmcgYXQgdGhlIHRvcCA1MCBmcm9tIGVhY2ggY2xhc3MKYGBge3J9CgpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNfdG9wXzUwID0gVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzWzE6NTAsXQpUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob190b3BfNTAgPSBUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob1sxOjUwLF0KCgpncm91cENhbGxzID0gcmJpbmQoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzX3RvcF81MCxUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob190b3BfNTApCgpZTkhlYXRtYXBfbWF0cml4ID0gTlVMTApZTkhlYXRtYXBfbWF0cml4ID0gdW5saXN0KGdyb3VwQ2FsbHNbMjoxMDAsMjo4Nl0pCllOSGVhdG1hcF9tYXRyaXggPSBhcy5udW1lcmljKHYpCllOSGVhdG1hcF9tYXRyaXggPSBtYXRyaXgodixucm93ID0gOTgsbmNvbCA9IDg0KQoKaGVhdG1hcChZTkhlYXRtYXBfbWF0cml4LHN5bW0gPSBGLG1haW4gPSAiSGVhdG1hcCB0ZXN0IixsYWJSb3cgPSByb3duYW1lcyhncm91cENhbGxzKSkKaW1hZ2UoWU5IZWF0bWFwX21hdHJpeCkKCmBgYAoKCmBgYHtyfQoKd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoWWVzX2NvbHVtbnNfc3ViKSxmaWxlID0gIlRGUl9tb2RlbF9yZXN1bHRzX3RvcF8xMDAwX2dlbmVzLmNzdiIpCndyaXRlLmNzdihhcy5kYXRhLmZyYW1lKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llcyksZmlsZSA9ICJURlJfbW9kZWxfcmVzdWx0c19ZZXNfZ2VuZXMuY3N2IikKVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzX3RvcF8yMDAwCndyaXRlLmNzdihhcy5kYXRhLmZyYW1lKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc190b3BfMjAwMCksZmlsZSA9ICJURlJfbW9kZWxfcmVzdWx0c19ZZXNfZ2VuZXNfdG9wXzIwMDAuY3N2IikKd3JpdGUuY3N2KGFzLmRhdGEuZnJhbWUoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfTm9fdG9wXzEwMDApLGZpbGUgPSAiVEZSX21vZGVsX3Jlc3VsdHNfTm9fZ2VuZXNfMTAwMC5jc3YiKQp3cml0ZS5jc3YoYXMuZGF0YS5mcmFtZShUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9Ob190b3BfMTAwMCksZmlsZSA9ICJURlJfbW9kZWxfcmVzdWx0c19Ob19nZW5lc18xMDAwLmNzdiIpCndyaXRlLmNzdihhcy5kYXRhLmZyYW1lKGdyb3VwQ2FsbHMpLGZpbGUgPSAiVEZSX21vZGVsX3Jlc3VsdHNfZ3JvdXAuY3N2IikKYGBgCgpOb3QgdXNlZApgYGB7cn0KI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJNM0MiKQojbGlicmFyeShNM0MpCgpZbWF0cml4TmV3ID0gWW1hdHJpeAojY29sbmFtZXMoWW1hdHJpeE5ldykgPSBjb2xuYW1lcyhUZmhUZnJfRXhvbl9BbGxHZW5lc19jb21iaW5lZF9ZZXNbLDI6ODVdKQp1bWFwKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc1ssMjo4Nl0sY29sdmVjID0gYXMuZmFjdG9yKGNvbG5hbWVzKFRmaFRmcl9FeG9uX0FsbEdlbmVzX2NvbWJpbmVkX1llc1ssMjo4Nl0pKSxsYWJlbHMgPSBhcy5mYWN0b3IoY29sbmFtZXMoVGZoVGZyX0V4b25fQWxsR2VuZXNfY29tYmluZWRfWWVzWywyOjg2XSkpKQoKI3Rmci51bWFwID0gdW1hcChZbWF0cml4KQojdGZyLnVtYXAKYGBgCgo=